package com.cvext.requiredvalidation.controls.ui;

import java.util.Iterator;

import javax.faces.component.UIComponent;
import javax.faces.component.UIComponentBase;
import javax.faces.component.UIForm;
import javax.faces.component.UIViewRoot;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseId;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

/**
 * @author hturksoy
 * 
 * Component that can walk the current UIForm for each apply-value, process-validation, and update-model phase.
 */
public abstract class ComponentFormWalker extends UIComponentBase {

	protected Log logger = LogFactory.getLog(getClass());
	
	public boolean isRendered() {
		// a ComponentFormWalker is generally not rendered
		return false;
	}
	
	private UIComponent findRootComponent(UIComponent component)
    {
        // parentForm is always a UIForm for HTML, but other rendering kits may need something different.
        UIComponent parentForm = component.getParent();
        while ( (false == (parentForm instanceof UIForm))
             && (false == (parentForm instanceof UIViewRoot))
             && (null != parentForm.getParent()) )
        {
            parentForm = parentForm.getParent();
        }
        
        return parentForm;
    }
	
	protected abstract void visitComponent(FacesContext context, UIComponent component, PhaseId phase);
	
	protected void visitComponentFacetsAndChildren(FacesContext context, UIComponent component, PhaseId phase)
    {
        visitComponent(context, component, phase);
        
        for (Iterator it = component.getFacetsAndChildren(); it.hasNext(); )
        {
            UIComponent childOrFacet = (UIComponent)it.next();
            
            if ( (childOrFacet.isRendered()) || (shouldVisitNonRenderedComponents()) )
            {
                visitComponentFacetsAndChildren(context, childOrFacet, phase);
            }
        }
    }

    protected boolean shouldVisitNonRenderedComponents()
    {
        return false;
    }
    
    protected boolean shouldScanDuringRestoreState(FacesContext context)
    {
        return false;
    }
    protected boolean shouldScanDuringApplyRequestValues(FacesContext context)
    {
        return false;
    }
    protected boolean shouldScanDuringProcessValidations(FacesContext context)
    {
        return false;
    }
    protected boolean shouldScanDuringUpdateModelValues(FacesContext context)
    {
        return false;
    }
    protected boolean shouldScanDuringSaveState(FacesContext context)
    {
        return false;
    }
   
    public void processDecodes(FacesContext context)
    {
        if (!shouldScanDuringApplyRequestValues(context))  return;
        
        if (context == null) throw new NullPointerException("context");

        UIComponent rootComponent = findRootComponent(this);
        visitComponentFacetsAndChildren(context, rootComponent, PhaseId.APPLY_REQUEST_VALUES);
    }

    public void processValidators(FacesContext context)
    {
        if (!shouldScanDuringProcessValidations(context))  return;
        
        if (context == null) throw new NullPointerException("context");

        UIComponent rootComponent = findRootComponent(this);
        visitComponentFacetsAndChildren(context, rootComponent, PhaseId.PROCESS_VALIDATIONS);
    }

    public void processUpdates(FacesContext context)
    {
        if (!shouldScanDuringUpdateModelValues(context))  return;

        if (context == null) throw new NullPointerException("context");

        UIComponent rootComponent = findRootComponent(this);
        visitComponentFacetsAndChildren(context, rootComponent, PhaseId.UPDATE_MODEL_VALUES);
    }
    
    public void processRestoreState(FacesContext context, Object state)
    {
        super.processRestoreState(context, state);
        
        if (!shouldScanDuringRestoreState(context))  return;

        if (context == null) throw new NullPointerException("context");

        UIComponent rootComponent = findRootComponent(this);
        visitComponentFacetsAndChildren(context, rootComponent, PhaseId.RESTORE_VIEW);
    }
    
    public Object processSaveState(FacesContext context)
    {
        Object result = super.processSaveState(context);
        
        if (!shouldScanDuringSaveState(context))  return result;

        if (context == null) throw new NullPointerException("context");

        UIComponent rootComponent = findRootComponent(this);
        visitComponentFacetsAndChildren(context, rootComponent, PhaseId.RENDER_RESPONSE);
    
        return result;
    }
}
